//******************************************************************************
//FireWork Launcher
//Version:  1.0   Last edited: 22.01.2012
//Comments: Implementation of Fireworks Launcher End Device.
//          
//
//Authors:  Denis Surkes  ID 321416505
//          Ran Levy      ID 036679215
//          Ela Niazov    ID 310259205
//          Ofer Simon    ID 039686951
//******************************************************************************
#pragma language=extended
#include <stdio.h>
#include "bsp.h"
#include "mrfi.h"
#include "bsp_leds.h"
#include "bsp_buttons.h"
#include "nwk_types.h"
#include "nwk_api.h"
#include "nwk_frame.h"
#include "nwk.h"
#include "msp430x22x4.h"
#include "vlo_rand.h"

#define DEG_MSG_SIZE 2        //2 byte - size of degree transmit value 
#define ACTION_MSG_SIZE 8     //Constant 8 byte transmit message size
#define TASK_NUMBER 3         //Number of tasks
#define BUTTON BIT2           //P1.2 push button on the board
#define TEMPERATURE_TRH 300   //30.0 degree  celsius

/* Stracts and enums */
typedef enum Priorities{E_RECEIV_ACT=0, E_SEND_TEMP=1, E_CONNECT=2}Priorities;
typedef enum statesType{E_READY=0,E_SUSPENDED,E_BLOCKED,E_RUNNING}statesType;
typedef enum actTypes{E_NOT_CHECKED=0, E_LAUNCH, E_ABORT}actTypes;
typedef struct TaskInfo
{
  void (*funcPtr)(void);    //pointer to task function
  statesType state;         //state of task
}TaskInfo;

/* Function declarations */
void MCU_Init(void);
void TaskInit(void);
void Port_Init(void);
void Address_Init(void);
void Join_Request(void);
void Read_Send_Temperature(void);
void Receive_Action(void);
void createRandomAddress(void);
void launch(int sequence);
void abort(void);
void Scheduler();

/* Global Variables */
const char* gpLaunch = "launch:";         //const launch word code 7 bytes
const char* gpAbort =  "abort: ";         //const abort word code 7 bytes
const char* gpTemperature =  "temp: ";    //const temp code 6 byte
linkID_t gLinkID1;                        //Global Link ID
TaskInfo taskArr[TASK_NUMBER];            //Task status array for the Scheduler
static uint8_t gSleepSem;                 //0 - cpu off no task to run
static uint8_t gSystemAbort;              //System in abort state, no action until restart 
static uint8_t gScbFlag;                  //Disable sCB interupt in abort state

/* callback handler */
static uint8_t sCB(linkID_t);             //Wireless interupt callback function

/* work loop semaphores */
static uint8_t sPeerFrameSem;
static uint8_t sJoinSem;

__no_init volatile int tempOffset @ 0x10F4; // Temperature offset set at production
__no_init volatile char Flash_Addr[4] @ 0x10F0; // Flash address set randomly

void main (void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  { 
  // delay loop to ensure proper startup before SimpliciTI increases DCO
  // This is typically tailored to the power supply used, and in this case
  // is overkill for safety due to wide distribution.
    volatile int i;
    for(i = 0; i < 0xFFFF; i++){}
  }
  gSystemAbort = 0;                       //Init system abort state
  BSP_Init();                             //Init BSP module
  MCU_Init();                             //Init Timers parameters 
  Port_Init();                            //Port configuration
  Address_Init();                         //Address initialization
  SMPL_Init(sCB);                         //Simplicity init
  TaskInit();                             //Define tasks priority 
  taskArr[E_CONNECT].state = E_READY;     //The first task to do
  gSleepSem++;
  while(1)
  {
    if(gSleepSem)
      Scheduler();                        //Run the Task if task is ready to run
    else
      __bis_SR_register(LPM3_bits+GIE);   //Cpu off until any interupt occurs
  }
}

/*------------------------------------------------------------------------------
* This function samples the temperature and sends it to the Base
------------------------------------------------------------------------------*/
void Read_Send_Temperature ()
{ 
  uint8_t  msg[ACTION_MSG_SIZE];
  volatile long temp;
  int degC, i;
  int result;
  
  BSP_TOGGLE_LED2();
  ADC10CTL1 = INCH_10 + ADC10DIV_4;       // Temp Sensor ADC10CLK/5
  ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE + ADC10SR;
  for( degC = 240; degC > 0; degC-- );    // delay to allow reference to settle
  ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
  __bis_SR_register(CPUOFF + GIE);        // LPM0 with interrupts enabled
  result = ADC10MEM;
  ADC10CTL0 &= ~ENC;
  
  // oC = ((A10/1024)*1500mV)-986mV)*1/3.55mV = A10*423/1024 - 278
  // the temperature is transmitted as an integer where 32.1 = 321
  // hence 4230 instead of 423
  temp = result;
  degC = ((temp - 673) * 4230) / 1024;
  if( tempOffset != 0xFFFF )
  {
    degC += tempOffset; 
  }
  /*-----------------------------------------------------------
  *     Deg Message format:  UB = upper Byte, LB = lower Byte *
  *     ---------------------------------------------         *
  *     | t | e | m | p | : |   | degC LB | degC UB |         *
  *     ---------------------------------------------         *
  *       0   1   2   3   4   5      6         7              *
  ------------------------------------------------------------*/
  //Message frame generator
  for (i = 0; i < ACTION_MSG_SIZE-2; i++)
    msg[i] = (uint8_t)gpTemperature[i]; 
  msg[i] = degC&0xFF;             //Last 2 bytes are for degree value
  msg[i+1] = (degC>>8)&0xFF;
  
  if (SMPL_SUCCESS == SMPL_Send(gLinkID1, msg, ACTION_MSG_SIZE))//Send the message
  {
    BSP_TOGGLE_LED2();
  }
  else
  {
    BSP_TOGGLE_LED2();
    BSP_TOGGLE_LED1();
  }
  
  if (degC > TEMPERATURE_TRH)   //Check the teperature threshhold
    abort();                    //Abort the system if temperature too high
}

/*------------------------------------------------------------------------------
* Receive action command from wireless
------------------------------------------------------------------------------*/
void Receive_Action()
{
  int act_type = E_NOT_CHECKED;
  int msgSequence;
  bspIState_t intState;
  if (sPeerFrameSem)
  {
    uint8_t     msgRx[MAX_APP_PAYLOAD], len;
    
    if (SMPL_Receive(gLinkID1, msgRx, &len) == SMPL_SUCCESS)
    {
      ioctlRadioSiginfo_t sigInfo;
      sigInfo.lid = gLinkID1;
      SMPL_Ioctl(IOCTL_OBJ_RADIO, IOCTL_ACT_RADIO_SIGINFO, (void *)&sigInfo);//Receive the action
      if (len == ACTION_MSG_SIZE)
      {
        act_type=E_LAUNCH;
        for (int i = 0; i < ACTION_MSG_SIZE-1 && act_type == E_LAUNCH; i++)
        {
          if ((char)msgRx[i] != gpLaunch[i])
          {
            act_type = E_NOT_CHECKED;
          }
        }
        if (act_type==E_LAUNCH)
        {
          msgSequence = (int)msgRx[ACTION_MSG_SIZE-1];
          launch(msgSequence);//LAUNCH!!
        }
        else
        {
          act_type=E_ABORT;
          for (int i = 0; i < ACTION_MSG_SIZE-1 && act_type == E_ABORT; i++)
          {
            if ((char)msgRx[i] != gpAbort[i])
            {
              act_type = E_NOT_CHECKED;
            }
          }
          if (act_type==E_ABORT)
          {
            abort(); //Abort the system
          }
        }
      }
    }
    BSP_ENTER_CRITICAL_SECTION(intState);
    sPeerFrameSem--;
    BSP_EXIT_CRITICAL_SECTION(intState);  
  }
}
  
/*------------------------------------------------------------------------------
* Launch action implementation
------------------------------------------------------------------------------*/
void launch(int sequence)
{
  uint8_t LedOut = (unsigned char)sequence;
  LedOut &= 0x1F;   //Only 5 LSB bits turn the 5 LEDS on the test LED board  
  P2OUT &= 0xE0;
  P2OUT |= LedOut;
}
/*------------------------------------------------------------------------------
* Abort action implementation
------------------------------------------------------------------------------*/
void abort()
{ 
  BSP_TURN_ON_LED2();   //Green LED is off
  BSP_TURN_ON_LED1();   //Red LED is off
  P2OUT &= 0x00;        //Turn off all Test board LEDS  
  gSystemAbort = 1;
  TACCTL0 &= ~CCIE;     //Stop all intrupts except PORT1 (P1.2)
  TBCCTL0 &= ~CCIE;
  __bis_SR_register(CPUOFF + GIE);
}

/*------------------------------------------------------------------------------
* Initialize task scheduler
------------------------------------------------------------------------------*/
void TaskInit(void)
{
  taskArr[E_RECEIV_ACT].funcPtr = Receive_Action;
  taskArr[E_RECEIV_ACT].state = E_SUSPENDED;
  taskArr[E_SEND_TEMP].funcPtr = Read_Send_Temperature;
  taskArr[E_SEND_TEMP].state = E_SUSPENDED;
  taskArr[E_CONNECT].funcPtr = Join_Request;
  taskArr[E_CONNECT].state = E_SUSPENDED;
}

/*------------------------------------------------------------------------------
* ADC10 interrupt service routine for temperature sample
------------------------------------------------------------------------------*/
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
  __bic_SR_register_on_exit(CPUOFF);        // Clear CPUOFF bit from 0(SR)
}

/*------------------------------------------------------------------------------
* PORT1 interrupt service routine for reset botton
------------------------------------------------------------------------------*/
#pragma vector=PORT1_VECTOR
__interrupt void Port1_ISR(void)
{
  if(P1IFG & BUTTON)
  {
    P1IFG &= ~BUTTON;
    WDTCTL = WDTPW + WDTCNTCL + WDTSSEL;    //System restart
    while(1)
    {}      
  }
  __bic_SR_register_on_exit(CPUOFF);        // Clear CPUOFF bit from 0(SR)
}

/*------------------------------------------------------------------------------
* Timer A0 interrupt service routine
------------------------------------------------------------------------------*/
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
  //This interupt turn on the CPU
  if (gScbFlag)
  {
    TACCTL0 &= ~CCIE;
    gScbFlag=0;
  }
  __bic_SR_register_on_exit(LPM3_bits);        // Clear LPM3 bit from 0(SR)
}

/*------------------------------------------------------------------------------
* Timer B0 interrupt service routine
------------------------------------------------------------------------------*/
#pragma vector=TIMERB0_VECTOR
__interrupt void Timer_B (void)
{
  taskArr[E_SEND_TEMP].state = E_READY;     //Time to send temperature sample
  gSleepSem++;
  __bic_SR_register_on_exit(LPM3_bits);        // Clear LPM3 bit from 0(SR)
}

/*------------------------------------------------------------------------------
* Runs in ISR context. Reading the frame should be done in the
* application thread not in the ISR thread.
------------------------------------------------------------------------------*/
static uint8_t sCB(linkID_t lid)
{
  __disable_interrupt();
  gScbFlag = 1;
  if(!gSystemAbort)
  {
    if (lid)
    {
      sPeerFrameSem++;
      taskArr[E_RECEIV_ACT].state = E_READY;
      gSleepSem++;
      TACCTL0 = CCIE;
    }
    else
    {
      sJoinSem++;
    }
  }
  __enable_interrupt();
  return 0;
}

/*------------------------------------------------------------------------------
* Init Function
------------------------------------------------------------------------------*/
void MCU_Init()
{
  BCSCTL1 = CALBC1_8MHZ;                    // Set DCO
  DCOCTL = CALDCO_8MHZ;
  
  /* Timer A initialize for temperature sampeling*/
  BCSCTL3 |= LFXT1S_2;                      // LFXT1 = VLO
  TACCTL0 = CCIE;                           // TACCR0 interrupt enabled
  TACCR0 = 12000;                           // ~1 second
  TACTL = TASSEL_1 + MC_1;                  // ACLK, upmode
  
  TBCCTL0 &= ~CCIE;                         // TBCCR0 interrupt disabled
  TBCCR0 = 24000;                           // ~2 second
  TBCTL = TBSSEL_1 + MC_1 + ID_1;                  // ACLK, upmode
  
//  /* UART initialize and configuration */
//  P3SEL |= 0x30;                            // P3.4,5 = USCI_A0 TXD/RXD
//  UCA0CTL1 = UCSSEL_2;                      // SMCLK
//  UCA0BR0 = 0x41;                           // 9600 from 8Mhz
//  UCA0BR1 = 0x3;
//  UCA0MCTL = UCBRS_2;                       
//  UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
//  IE2 |= UCA0RXIE;                          // Enable USCI_A0 RX interrupt
  __enable_interrupt(); 
}

/*------------------------------------------------------------------------------ 
* Ports configuration 
------------------------------------------------------------------------------*/
void Port_Init()
{
  /*
  * Configuration of Port 1
  * BIT0 - Red onboard LED
  * BIT1 - Green onboard LED
  * BIT2 (BUTTON) on board button (pulled down)
  */  
  P1SEL &= 0x0;                                  // 0- I/O function is selected
  P1DIR &= 0x0;                                  // Port 1 input direction (BUTTON)
  P1DIR |= BIT0+BIT1;                            // P1.0 and P1.1 output direction (LEDs)  
  P1REN |= BUTTON;                               // P1.2 pullup/pulldown resistor Enabled
  P1OUT |= BUTTON;                               // P1.2 set to logic high level
  P1IES |= BUTTON;                               // P1.2 high-to-low transition
  P1IFG &= ~BUTTON;                              // P1.2 clear any pendings interupts
  P1IE &= 0x0;                                   // Disable all other interupts on Port 1
  P1IE |= BUTTON;                                // P1.2 interupt enable
  /*
  * Configuration of Port 2
  * BIT0:4 - 5 Red LEDs connected to onboard pin outputs
  * BIT6:7 - Simplicity I/O ports
  */  	
  P2SEL &= 0x00;                                 // 0- I/O function is selected                                 
  P2REN &= 0x00;                                 // pullup/pulldown resistor disabled
  P2DIR = BIT0+BIT1+BIT2+BIT3+BIT4;              //0x1F set as output
  P2OUT &= 0x00;         			         // P2 clear all ther outputs 
  
  P3DIR = 0xC0;
  P3OUT = 0x00;
  P4DIR = 0xFF;
  P4OUT = 0x00;
}

/*------------------------------------------------------------------------------
* Address configuration
------------------------------------------------------------------------------*/
void Address_Init()
{
  addr_t lAddr;
  if( CALBC1_8MHZ == 0xFF )                 // Do not run if cal values are erased
  {
    volatile int i;
    BSP_TURN_ON_LED1();
    BSP_TURN_OFF_LED2();
    while(1)
    {
      for(i = 0; i < 0x5FFF; i++){}
      BSP_TOGGLE_LED2();
      BSP_TOGGLE_LED1();
    }
  }
  
  
  if( Flash_Addr[0] == 0xFF && 
      Flash_Addr[1] == 0xFF && 
      Flash_Addr[2] == 0xFF && 
      Flash_Addr[3] == 0xFF )
  {
    createRandomAddress();                  // set Random device address at initial startup
  }
  lAddr.addr[0]=Flash_Addr[0];
  lAddr.addr[1]=Flash_Addr[1];
  lAddr.addr[2]=Flash_Addr[2];
  lAddr.addr[3]=Flash_Addr[3];
  SMPL_Ioctl(IOCTL_OBJ_ADDR, IOCTL_ACT_SET, &lAddr);
  BCSCTL1 = CALBC1_8MHZ;                    // Set DCO after random function
  DCOCTL = CALDCO_8MHZ;
}

/*------------------------------------------------------------------------------ 
* Create random address and save it in flash memory
------------------------------------------------------------------------------*/
void createRandomAddress()
{
  unsigned int rand, rand2;
  do
  {
    rand = TI_getRandomIntegerFromVLO();    // first byte can not be 0x00 of 0xFF
  }
  while( (rand & 0xFF00)==0xFF00 || (rand & 0xFF00)==0x0000 );
  rand2 = TI_getRandomIntegerFromVLO();
  
  BCSCTL1 = CALBC1_1MHZ;                    // Set DCO to 1MHz
  DCOCTL = CALDCO_1MHZ;
  FCTL2 = FWKEY + FSSEL0 + FN1;             // MCLK/3 for Flash Timing Generator
  FCTL3 = FWKEY + LOCKA;                    // Clear LOCK & LOCKA bits
  FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation

  Flash_Addr[0]=(rand>>8) & 0xFF;
  Flash_Addr[1]=rand & 0xFF;
  Flash_Addr[2]=(rand2>>8) & 0xFF; 
  Flash_Addr[3]=rand2 & 0xFF; 
  
  FCTL1 = FWKEY;                            // Clear WRT bit
  FCTL3 = FWKEY + LOCKA + LOCK;             // Set LOCK & LOCKA bit
}

/*------------------------------------------------------------------------------ 
* Sending Join Request
------------------------------------------------------------------------------*/
void Join_Request()
{
  // keep trying to join until successful. toggle LEDS to indicate that
  // joining has not occurred.
  while (SMPL_NO_JOIN == SMPL_Init((uint8_t (*)(linkID_t))0))
  {
    BSP_TOGGLE_LED1();
    BSP_TOGGLE_LED2();
    __bis_SR_register(LPM3_bits + GIE);     // LPM3 with interrupts enabled
  }
  // keep trying to link...
  while (SMPL_SUCCESS != SMPL_Link(&gLinkID1))
  {
    __bis_SR_register(LPM3_bits + GIE);     // LPM3 with interrupts enabled
    BSP_TOGGLE_LED1();
    BSP_TOGGLE_LED2();
    
  }
  // Turn off all LEDs
  if (BSP_LED1_IS_ON())
  {
    BSP_TOGGLE_LED1();
  }
  if (BSP_LED2_IS_ON())
  {
    BSP_TOGGLE_LED2();
  }
  //New Timer A configuration for scheduler
  TACCTL0 &= ~CCIE;                         // TACCR0 interrupt Disabled
  TACCR0 = 120;                             // ~0.01 second
  TBCCTL0 = CCIE;                           // TBCCR0 interrupt Enabled
}
/*------------------------------------------------------------------------------ 
* Scheduler implementation: 
* Pass through the task array and run the first occurence of the Ready task 
------------------------------------------------------------------------------*/
void Scheduler()
{
  int index;
  uint8_t taskLaunched = 0;
  __disable_interrupt();
  //launch task by order in task Array (Task in Raedy state with the smalest index) 
  for (index = 0; index < TASK_NUMBER && !taskLaunched; index++)
  {
    if (taskArr[index].state == E_READY)
    {
      taskArr[index].funcPtr();
      gSleepSem--;
      taskArr[index].state = E_SUSPENDED;
      taskLaunched = 1;
    }
  }
  __enable_interrupt();
}
